package serialPort

import (
	"errors"
	"fmt"
	"gitee.com/fierce_wolf/go-fox-edge-common/commLogger"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Signal"
	"github.com/jacobsa/go-serial/serial"
	"io"
	"time"
)

type SerialPort struct {
	serialPort   io.ReadWriteCloser
	serialBuffer *serialBuffer
	Signal       *Signal.Lock
}

func (e *SerialPort) Open(options serial.OpenOptions) error {
	// 打开串口
	port, err := serial.Open(options)
	if err != nil {
		return err
	}
	e.serialPort = port

	// 分配承载接收数据的缓存，默认64K大小
	e.serialBuffer = &serialBuffer{}
	e.serialBuffer.instance(64 * 1024)

	// 创建一个监听线程
	go e.listener()

	return nil
}

func (e *SerialPort) IsOpen() bool {
	return e.serialBuffer != nil
}

func (e *SerialPort) Close() error {
	e.serialBuffer = nil
	return e.serialPort.Close()
}

func (e *SerialPort) Write(data []byte) (int, error) {
	return e.serialPort.Write(data)
}

func (e *SerialPort) Read(data []byte, timeout int64) (int, error) {
	buffer := e.serialBuffer
	if buffer == nil {
		return 0, errors.New("串口未打开")
	}

	startTime := time.Now().UnixMilli()

	sum := 0
	for true {
		count := buffer.Count()
		nowTime := time.Now().UnixMilli()

		// 场景1：超时范围之内，还没有数据到达
		if count == 0 && nowTime-startTime < timeout {
			time.Sleep(10 * time.Millisecond)
			continue
		}
		// 场景2：达到了最大超时，还没有数据到达
		if count == 0 && nowTime-startTime >= timeout {
			return 0, nil
		}
		// 场景3：达到了最大超时，已经有部分数据到达
		if count > 0 && nowTime-startTime < timeout {
			if count != sum {
				// 数据还在增加，那么继续等待后续数据到达
				sum = count
				time.Sleep(10 * time.Millisecond)
				continue
			} else {
				// 数据不再增加，那么后续没有数据了
				return buffer.Read(data), nil
			}
		}
	}

	return 0, nil
}

// 监听线程函数
func (e *SerialPort) listener() {
	// 定义一个函数级的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			commLogger.Error(fmt.Sprintf("listener() 捕获到异常:%v", r))
		}
	}()

	buffer := e.serialBuffer

	bytes := make([]byte, 4096)
	for true {
		// 检测：绑定的缓存对象，是否因为关闭而失效
		if e.serialBuffer == nil {
			return
		}

		// 读取数据，注意，这是自带阻塞几秒的读取
		cnt, err := e.serialPort.Read(bytes)
		if err != nil {
			return
		}

		if cnt == 0 {
			continue
		}

		// 保存数据到缓存
		buffer.append(bytes, cnt)

		if e.Signal != nil {
			e.Signal.Signal()
		}
	}
}
